#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include "socket_ipv6.h"

// #define PORT "8080"
// webcache est le port 8080 (cf /etc/services)
/*#define PORT "webcache"
#define HOST "localhost"*/
#define TAILLE_BUFF 100


int RecoieDonnees ( int fd , int sock )
{

    char buff [TAILLE_BUFF ] ;
    int res , res2 ;
    int nbrecu = 0 ;
    while ( 1 )
    {
        res = recv ( sock , buff , TAILLE_BUFF, 0 ) ;
        if ( res < 0) {
            perror ( "Probleme ~A la lecture du fichier" ) ;
            return -1;
        }
        if ( res == 0) {
            // Le f i c h i e r e s t termin˜A°c
            break ;
        }
        nbrecu += res ;
        // f p r i n t f ( s t d e r r , ”Recu %d oc t t o t a l %d oc t \n” , res , nb r e cu ) ;
        res2 = write ( fd , buff , res ) ;
        if ( res != res2 ) {
            perror ( "Probleme ~A l’~A c°criture du fichier" ) ;
            return -1;
            }
    }
    return nbrecu ;
}

int socket_ipv6 ( Parametres * P )
{
  // structure pour faire la demande
  struct addrinfo hints;
  // structure pour stocker et lire les résultats
  struct addrinfo *result, *rp;
  // socket  (s)
  int s=-1;
  int socket_donnees=-1;
//  int fd =2;
  // variables pour tester si les fonctions donnent un résultats ou une erreur
  int res;
  int bon;
  // Des variable pour contenir de adresse de machine et des numero de port afin de les afficher
  char hname[NI_MAXHOST], sname[NI_MAXSERV];


  // on rempli la structure hints de demande d'adresse
  memset(&hints, 0, sizeof(struct addrinfo));
  hints.ai_family = AF_UNSPEC;    /* IPv4 ou IPv6 */
  hints.ai_socktype = SOCK_STREAM; /* socket flux connectée */
  hints.ai_flags = 0;
  hints.ai_protocol = 0;          /* Any protocol */
  hints.ai_addrlen = 0;
  hints.ai_addr = NULL;
  hints.ai_canonname = NULL;
  hints.ai_next = NULL;

  res = getaddrinfo(P->serveur, P->port, &hints, &result);
  if (res != 0) { // c'est une erreur
    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
    exit(1);
  }

  // si res = 0 le véritable résultat de la fontion est l'argument result
  // qui contient une liste d'addresse correspondant à la demande on va les
  // rester jusqu'a trouver une qui convient
  rp = result;
  bon = 0;
  while (rp != NULL) {
    // on parcourt la liste pour en trouver une qui convienne

    // on récupère des informations affichables
    res = getnameinfo(rp->ai_addr, rp->ai_addrlen,
		      hname, NI_MAXHOST,
		      sname, NI_MAXSERV,
		      NI_NUMERICSERV|NI_NUMERICHOST);
    if (res != 0) {
      fprintf(stderr, "getnameinfo: %s\n", gai_strerror(res));
      exit (1);
    }
    fprintf (stderr, "On tente l'adresse %s sur le port %s .....",
	    hname, sname);

    // on essaye
    s = socket(rp->ai_family, rp->ai_socktype,rp->ai_protocol);
    // si le résultat est -1 cela n'a pas fonctionné on recommence avec la prochaine
    if (s == -1) {
      perror("Création de la socket");
      continue;
    }

    // si la socket a été obtenue, on essaye de se connecter
    res = connect(s, rp->ai_addr, rp->ai_addrlen);
    if (res == 0 ) {// cela a fonctionné on est connecté
      bon = 1;
      fprintf (stderr, "OK\n");
      break;
    }
    else { // sinon le bind a été impossible, il faut fermer la socket
      perror("Imposible de se connecter");
      close (s);
    }

    rp = rp->ai_next;
  }

  if (bon == 0) { // Cela n'a jamais fonctionné
    fprintf(stderr, "Aucune connexion possible\n");
    exit(1);
  }



  freeaddrinfo(result);           /* No longer needed */



  // ######################################################
  // (5) Dialogue
  // Une fois la connexion établie, le serveur et le client peuvent s'échanger des messages
  // envoyés par write() ou send() et lus par read()ou recv() selon un protocole établi.
  // Ici nous utiliseront le protocole suivant : seul le client écrit, le serveur lit octet
  // par octet
  // ######################################################
  while (1) {
    char buff[TAILLE_BUFF];

        fgets(buff, TAILLE_BUFF, stdin);
        // Le dernier carractère est un retour chariot
        buff[strlen(buff)-1] = '\0';

        if (strcmp(buff, "fin")==0) {
        // Quand l'utilisateur tape fin on sort
        break;
          }
        if ( strcmp( buff, "lister") == 0)
        {
            res = getaddrinfo(P->serveur, P->port, &hints, &result);
              if (res != 0) { // c'est une erreur
                fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
                exit(1);
              }


              // on récupère des informations affichables
            res = getnameinfo(rp->ai_addr, rp->ai_addrlen,
		      hname, NI_MAXHOST,
		      sname, NI_MAXSERV,
		      NI_NUMERICSERV|NI_NUMERICHOST);
                   if (res != 0) {
                    fprintf(stderr, "getnameinfo: %s\n", gai_strerror(res));
                      exit (1);
                    }
                    fprintf (stderr, "On tente l'adresse %s sur le port %s .....",
                        hname, sname);

            socket_donnees = socket(rp->ai_family, rp->ai_socktype,rp->ai_protocol);
            res = connect(s, rp->ai_addr, rp->ai_addrlen);
            if (res == 0 ) {// cela a fonctionné on est connecté
              bon = 1;
              fprintf (stderr, "OK\n");
              break;
            }
            else { // sinon le bind a été impossible, il faut fermer la socket
              perror("Imposible de se connecter");
              close (s);
            }
            res= send( socket_donnees, "LIST", (strlen("LIST")+1)*sizeof(char), MSG_NOSIGNAL);

            //res = RecoieDonnees ( STDOUT_FILENO , socket_donnees );

        }
        //res = send(s, (char*)buff, (strlen(buff)+1)*sizeof(char), MSG_NOSIGNAL);
        // s le socket sur laquel on écrit
        // buff le message écrit
        // (strlen(buff)+1)*sizeof(char) la longueur du message
        // MSG_NOSIGNAL permet de choisir la façon dont le système réagira si la conexion est brisée
        // lors du send. Dans ce cas, par defaut, le send génère un signal, il faudrait donc prévoir le cas
        // et le traiter avec un sigaction. Or je n'ai pas envie de le faire.
        // Avec l'option MSG_NOSIGNAL, la fonction send s'arrèterra sur une erreur ce qui est plus
        // simple à traiter.
        if (res < 0)
        {
          perror("erreur dans le send");
          break;
        }
        fprintf( stdout, "%d \n", res);

  }


  // ######################################################
  // (6) Fermeture de la connexion
  // ######################################################
  if (close(s)< 0) {
    perror("Problème à la fermeture de la socket");
  }

  fprintf(stdout, "bye\n");
  return 0;
}

